package it.polimi.swKnights.SWIMv2.ejb.friendsEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import it.polimi.swKnights.SWIMv2.ejb.authentication.LoginHandlerLocal;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.Friendship;
import it.polimi.swKnights.SWIMv2.ejb.entityBeans.RegisteredUser;
import it.polimi.swKnights.SWIMv2.ejb.exceptions.InvalidLoginPrivilegesException;
import it.polimi.swKnights.SWIMv2.ejb.exceptions.LoginFailException;
import it.polimi.swKnights.SWIMv2.ejb.personalData.PersonalDataManagerLocal;
import it.polimi.swKnights.SWIMv2.ejb.searchEngine.ResultCategory;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * Session Bean implementation class FriendshipManager
 */
@Stateless
public class FriendshipManager implements FriendshipManagerRemote,
		FriendshipManagerLocal
{

	@PersistenceContext(unitName = "swimv2-manager")
	private EntityManager manager;

	@EJB
	private LoginHandlerLocal loginController;

	@EJB
	private PersonalDataManagerLocal dataManager;

	@Override
	public void addFriendship(String userName1, String userName2, boolean direct)
	{
		RegisteredUser user1 = this.manager.getReference(RegisteredUser.class,
				userName1);
		RegisteredUser user2 = this.manager.getReference(RegisteredUser.class,
				userName2);
		Friendship friendship = new Friendship(user1, user2, direct);

		this.manager.persist(friendship);
	}

	@Override
	public List<Map<String, String>> getFriendsList(String userName,
			String password) throws LoginFailException,
			InvalidLoginPrivilegesException
	{
		this.loginController.checkLoginDataAndPrivileges(userName, password,
				RegisteredUser.class);

		RegisteredUser user = this.manager.find(RegisteredUser.class, userName);

		return this.dataManager.getPersonalDataFromUser(user.getFriends(),
				ResultCategory.friend);
	}

	@Override
	public boolean isFriend(String userName, String password, String friend)
			throws LoginFailException, InvalidLoginPrivilegesException
	{
		this.loginController.checkLoginDataAndPrivileges(userName, password,
				RegisteredUser.class);
		return this.areFriend(userName, friend);
	}

	@Override
	public List<Map<String, String>> getSuggestedFriends(String userName,
			String password) throws LoginFailException,
			InvalidLoginPrivilegesException
	{
		this.loginController.checkLoginDataAndPrivileges(userName, password,
				RegisteredUser.class);

		List<RegisteredUser> suggestedFriendsList = getSuggestedFriends(userName);

		return this.dataManager.getPersonalDataFromUser(suggestedFriendsList,
				ResultCategory.suggested);

	}

	@Override
	public boolean areFriend(String user1, String user2)
	{
		RegisteredUser user = this.manager.getReference(RegisteredUser.class, user1);
		RegisteredUser possibleFriend = this.manager.getReference(
				RegisteredUser.class, user2);
		return user.getFriends().contains(possibleFriend);
	}

	private List<RegisteredUser> getSuggestedFriends(String userName)
	{
		List<RegisteredUser> suggestedFriendsList = new ArrayList<RegisteredUser>();
		String suggestedFriendsQuery = ""
				// Select friends...
				+ "SELECT DISTINCT fof FROM RegisteredUser AS fof, RegisteredUser AS friend, "
				+ "Friendship AS f, Friendship AS frf "
				+ "WHERE ((f.userName1 = friend.userName AND f.userName2 = :userName) "
				+ "OR (f.userName1 = :userName AND f.userName2 = friend.userName))"
				// ... of direct ...
				+ "AND f.direct = TRUE "
				// ... friends
				+ "AND ((frf.userName1 = fof.userName AND frf.userName2 = friend.userName) "
				+ "OR (frf.userName1 = friend.userName AND frf.userName2 = fof.userName)) "
				// but not me...
				+ "AND fof.userName != :userName "
				// ...and not users that are already friends...
				+ "AND fof.userName != ALL "
				+ "(SELECT af.userName FROM RegisteredUser af, Friendship AS f "
				+ "WHERE (f.userName1 = af.userName AND f.userName2 = :userName) "
				+ "OR (f.userName1 = :userName AND f.userName2 = af.userName)) "
				// ...and also not the user to which i have pending send
				// requests!
				+ "AND fof.userName != ALL "
				+ "(SELECT u.userName FROM RegisteredUser AS u, FriendshipRequest AS fr "
				+ "WHERE fr.applicantName = :userName AND fr.requestedName = u.userName)";

		Query suggestedFriends = this.manager
				.createQuery(suggestedFriendsQuery);
		suggestedFriends.setParameter("userName", userName);
		List<?> itemList = suggestedFriends.getResultList();
		for (Object item : itemList)
		{
			RegisteredUser user = (RegisteredUser) item;
			suggestedFriendsList.add(user);
		}
		return suggestedFriendsList;
	}

}
